<?php
final class Mag {

	private static $_app;

	private static $_registry = array();

	public static $_providers = array();

	/**
	 * Run website
	 */
	public static function run() {
		self::$_app = new Core_Model_App();
		self::$_app->run();
	}

	/**
	 * Retrieve instance of helper
	 *
	 * @param   string $className
	 * @return  mixed
	 */
	public static function helper($module, $name='') {
		$className = $module . '_Helper_' . (empty($name) ? 'Default' : $name);
		return self::singleton($className);
	}

	/**
	 * Retrieve instance of class
	 *
	 * @param   string $className
	 * @param   array $arguments
	 * @return  mixed
	 */
	public static function object() {
		$args = func_get_args();
		$method = new ReflectionMethod(get_class(self::$_app), 'getOjectInstance');
		return $method->invokeArgs(self::$_app, $args);
	}

	/**
	 * Retrieve instance of class (singleton)
	 *
	 * @param   string $className
	 * @param   array $arguments
	 * @return  mixed
	 */
	public static function singleton() {
		$args = func_get_args();
		$key = '_singleton/'.$args[0];
		if (!self::registry($key)) {
			$method = new ReflectionMethod(get_class(self::$_app), 'getOjectInstance');
			$instance = $method->invokeArgs(self::$_app, $args);
			self::registry($key, $instance);
		}
		return self::registry($key);
	}

	/**
	 * Retrieve a value from registry by a key
	 * Register a new variable
	 * Unregister a variable from register by key
	 *
	 * @param string $key
	 * @param mixed $value
	 * @return mixed
	 */
	public static function registry() {
		$args = func_get_args();
		$n = count($args);
		
		if ($n == 2) {
			$name = $args[0];
			$value = $args[1];

			if ($value===null) {
				unset(self::$_registry[$name]);
			} else {
				self::$_registry[$name] = $value;
			}
		} elseif ($n == 1) {
			$name = $args[0];
			return isset(self::$_registry[$name]) ? self::$_registry[$name] : null;
		}
	}

	public static function trigger($event, $data=null) {
		// trigger event
	}

	public static function registerProvider($provider) {
		if ( in_array($provider , self::$_providers) ) return; 
		array_unshift(self::$_providers, $provider);
	}
	
	public static function removeProvider($provider) {
		if (( $key = array_search($provider, self::$_providers) ) !== false ) {
			unset(self::$_providers[$key]);
		}
	}

	private static function _callProviderMethod($method) {
		foreach(self::$_providers as $provider) {
			if (is_callable(array($provider, $method)))
				return $provider->$method();
		}
		return null;
	}
	
	/**
	 * Retrieve global config object
	 *
	 * @param   string $key
	 * @return  mixed
	 */
	public static function getConfig() {
		return self::_callProviderMethod(__FUNCTION__);
	}

	/**
	 * Retrieve database connection instance
	 *
	 * @return Core_Model_Db_Connection
	 */
	public static function getConnection() {
		return self::_callProviderMethod(__FUNCTION__);
	}

	public static function getRequest() {
		return self::_callProviderMethod(__FUNCTION__);
	}

	public static function getResponse() {
		return self::_callProviderMethod(__FUNCTION__);
	}

	public static function getController() {
		return self::registry('controller');
	}

	public static function getUrl($routePath = null, $routeParams = null) {
		return self::$_app->getUrl($routePath, $routeParams);
	}

	/**
	 * Check website has been installed
	 *
	 * @return boolean
	 */
	public static function isInstalled() {
		$configFile = BP . DS . 'code' . DS . 'config.php';
		return is_readable($configFile);
	}

	/**
	 * Retrieve code directory absolute path
	 *
	 * @return string
	 */
	public static function getCodeDir() {
		return BP . DS . 'code';
	}

	/**
	 * Retrieve include file name from class name
	 *
	 * @param  string $className
	 * @return string
	 */
	public static function getIncludeFilename($className) {
		return Mag::getCodeDir() . DS . str_replace('_', DS, $className) . '.php';
	}
	
}